package cn.iocoder.yudao.module.im.service.member;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.module.im.controller.app.member.vo.AppMemberMessagePageReqVO;
import cn.iocoder.yudao.module.im.controller.app.member.vo.AppMemberMessageSaveReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.member.MemberConversationDO;
import cn.iocoder.yudao.module.im.dal.mysql.group.GroupMapper;
import cn.iocoder.yudao.module.im.dal.mysql.member.MemberConversationMapper;
import cn.iocoder.yudao.module.im.enums.ImMessageContentTypeEnum;
import cn.iocoder.yudao.module.im.enums.ImSendTypeEnum;
import cn.iocoder.yudao.module.im.enums.WebSocketMessageTypeConstants;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
import cn.iocoder.yudao.module.mp.api.user.MpUserApi;
import cn.iocoder.yudao.module.mp.api.user.dto.UserMpTemplateMsgSendReqDTO;
import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendTypeEnum;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.trade.api.config.TradeConfigApi;
import cn.iocoder.yudao.module.trade.api.config.dto.TradeConfigRespDTO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.module.im.controller.admin.member.vo.*;
import cn.iocoder.yudao.module.im.dal.dataobject.member.MemberMessageDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.im.dal.mysql.member.MemberMessageMapper;

import java.time.LocalDateTime;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.im.enums.MessageTemplateConstants.UNREAD_MESSAGE;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;

/**
 * IM消息 Service 实现类
 *
 * @author Jerry
 */
@Service
@Validated
public class MemberMessageServiceImpl implements MemberMessageService {

    @Resource
    private MemberMessageMapper memberMessageMapper;

    @Resource
    private GroupMapper groupMapper;

    @Resource
    private MemberConversationMapper memberConversationMapper;

    @Resource
    private WebSocketSenderApi webSocketSenderApi;

    @Resource
    public SocialClientApi socialClientApi;

    @Resource
    private MpUserApi mpUserApi;

    @Resource
    private TradeConfigApi tradeConfigApi;

    @Resource
    private MemberPointApi memberPointApi;

    @Resource
    private MemberUserApi memberUserApi;

    @Override
    public Long createMemberMessage(MemberMessageSaveReqVO createReqVO) {
        // 插入
        MemberMessageDO memberMessage = BeanUtils.toBean(createReqVO, MemberMessageDO.class);
        memberMessageMapper.insert(memberMessage);
        // 返回
        return memberMessage.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long sendImMessage(AppMemberMessageSaveReqVO createReqVO) {
        MemberUserRespDTO member = memberUserApi.getUser(getLoginUserId());

        if(CommonStatusEnum.isDisable(member.getStatus())){
            throw exception(AUTH_LOGIN_USER_DISABLED);
        }

        TradeConfigRespDTO config = tradeConfigApi.getTradeConfig();
        if(!config.getAuthEnabled()){
            if(StringUtils.isBlank(member.getPhoto())){
                throw exception(USER_PHOTO_EMPTY);
            }
        }

        if(!config.getAuthEnabled() && config.getQrcodeEnabled()){
            if(!mpUserApi.getQrcode(getLoginUserId())){
                throw exception(USER_FANS_QRCODE);
            }
        }

        if(member.getDisabledTime() != null){
            LocalDateTime now = DateUtil.toLocalDateTime(DateUtil.date());
            if(member.getDisabledTime().isAfter(now)){
                throw exception(IM_DISABLED_TIME, DateUtil.formatDateTime(new DateTime(member.getDisabledTime())));
            }
        }

        Integer point = 20;
        if(SexEnum.FEMALE.getSex().equals(member.getSex())){
            point = 0;
        }
        GroupDO groupDO = groupMapper.selectById(createReqVO.getGroupId());

        MemberMessageDO firstMessage = memberMessageMapper.getFirstMessage(createReqVO.getGroupId());
        MemberMessageDO lastMessage = memberMessageMapper.getLastMessage(createReqVO.getGroupId());

        if(!groupDO.getIsMatch()){

            if(member.getPoint() < point){
                if(member.getVipEndTime() == null){
                    throw exception(IM_VIP_NOT_EXISTS);
                }else{
                    if(member.getVipEndTime().isBefore(DateUtil.toLocalDateTime(DateUtil.date()))){
                        throw exception(IM_VIP_NOT_EXISTS);
                    }else{
                        point = 0;
                    }
                }
            }

            if(ImMessageContentTypeEnum.IMAGE.getType().equals(createReqVO.getContentType())){
                point = 200;
                if(member.getPoint() < point){
                    if(member.getVipEndTime() == null){
                        throw exception(IMAGE_VIP_NOT_EXISTS);
                    }else{
                        if(member.getVipEndTime().isBefore(DateUtil.toLocalDateTime(DateUtil.date()))){
                            throw exception(IMAGE_VIP_NOT_EXISTS);
                        }else{
                            point = 0;
                        }
                    }
                }
            }

        }

        // 更新在线时间
        memberUserApi.updateUserLogin(getLoginUserId(), getClientIP());


        // 判断是否是垃圾消息
        boolean repeatCount = memberMessageMapper.getRepeatCount(createReqVO.getSendUserId(), createReqVO.getContent());
        if(repeatCount){
            // 禁言
            memberUserApi.DisabledUser(createReqVO.getSendUserId());
        }

        // 判断是否被拉黑
        MemberConversationDO conversationDO = memberConversationMapper.getMemberConversationByByUserIdAndGroupId(createReqVO.getReceiveUserId(), createReqVO.getGroupId());
        if(conversationDO.getSlashed().equals("1")){
            throw exception(IM_SLASHED_DISABLED);
        }

        checkMessage(createReqVO);

        // 插入
        MemberMessageDO memberMessage = BeanUtils.toBean(createReqVO, MemberMessageDO.class);
        memberMessage.setSendType(ImSendTypeEnum.USER.getType());
        memberMessageMapper.insert(memberMessage);

        // 更新已读
        memberMessageMapper.updateMessageRead(createReqVO.getGroupId(), createReqVO.getReceiveUserId());

        // 更新会话内容
        List<MemberConversationDO> conversationList = memberConversationMapper.getMemberConversationByGroupId(createReqVO.getGroupId());
        for (MemberConversationDO conversation : conversationList){
            conversation.setLastMessageContentType(createReqVO.getContentType());
            conversation.setLastMessageContent(createReqVO.getContent());
            conversation.setLastMessageTime(DateUtil.toLocalDateTime(DateUtil.date()));
            conversation.setLastMessageReadStatus("0");
            if(createReqVO.getReceiveUserId().equals(conversation.getByUserId())){
                conversation.setStatus(CommonStatusEnum.ENABLE.getStatus());
                // 更新未读
                Long unreadMessageCount = memberMessageMapper.UnreadMessageCount(createReqVO.getGroupId(), createReqVO.getReceiveUserId());
                if(unreadMessageCount != null){
                    conversation.setUnreadMessageCount(unreadMessageCount.intValue());
                }
            }else{
                conversation.setSendMessageCount(conversation.getSendMessageCount()+1);
                conversation.setUnreadMessageCount(0);
            }
            memberConversationMapper.updateById(conversation);
        }

        if(!groupDO.getIsMatch() && point > 0){
            if(firstMessage == null){
                // 消耗积分
                memberPointApi.reducePoint(createReqVO.getSendUserId(), point, MemberPointBizTypeEnum.SEND_MSG.getType(), String.valueOf(memberMessage.getId()));
            }else{
                if(firstMessage.getSendUserId().equals(createReqVO.getSendUserId())){
                    // 消耗积分
                    memberPointApi.reducePoint(createReqVO.getSendUserId(), point, MemberPointBizTypeEnum.SEND_MSG.getType(), String.valueOf(memberMessage.getId()));
                }else {
                    if(createReqVO.getSendUserId().equals(lastMessage.getReceiveUserId())){
                        // 奖励积分
                        memberPointApi.addPoint(createReqVO.getSendUserId(), point/2, MemberPointBizTypeEnum.REPLY_MSG.getType(), String.valueOf(memberMessage.getId()));
                    }
                }
            }
        }

        getSelf().sendAsyncMessageToMember(memberMessage.getReceiveUserId(), WebSocketMessageTypeConstants.IM_MESSAGE_NEWS, memberMessage);

        getSelf().sendAsyncMessageToMember(memberMessage.getReceiveUserId(), member.getNickname(), memberMessage.getGroupId());
        // 返回
        return memberMessage.getId();
    }

    /**
     * 内容审核
     * @param createReqVO
     */
    private void checkMessage(AppMemberMessageSaveReqVO createReqVO) {
        boolean check = true;
        if(ImMessageContentTypeEnum.TEXT.getType().equals(createReqVO.getContentType())){
            try {
                check = tradeConfigApi.checkMessage(createReqVO.getContent(), String.valueOf(createReqVO.getReceiveUserId()));
            }catch (Exception e){
                throw exception(MEMBER_MESSAGE_ERROR);
            }
            if(!check){
                throw exception(MEMBER_MESSAGE_STOP);
            }
        }else if(ImMessageContentTypeEnum.IMAGE.getType().equals(createReqVO.getContentType())){
            try {
                check = tradeConfigApi.checkImg(createReqVO.getContent(), String.valueOf(createReqVO.getReceiveUserId()));
            }catch (Exception e){
                throw exception(MEMBER_MESSAGE_ERROR);
            }

            if(check){
                String url = socialClientApi.getQrCode(createReqVO.getContent());
                if(StringUtils.isNotBlank(url)){
                    if(!url.contains("https://u.wechat.com")){
                        check = false;
                    }
                }
            }

            if(!check){
                throw exception(MEMBER_MESSAGE_STOP);
            }
        }else if(ImMessageContentTypeEnum.VOICE.getType().equals(createReqVO.getContentType())){
            try {
                check = tradeConfigApi.checkVoice(createReqVO.getContent(), String.valueOf(createReqVO.getReceiveUserId()));
            }catch (Exception e){
                throw exception(MEMBER_MESSAGE_ERROR);
            }
            if(!check){
                throw exception(MEMBER_MESSAGE_STOP);
            }
        }
    }

    @Async
    public void sendAsyncMessageToMember(Long userId, String messageType, Object content) {
        webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), userId, messageType, content);
    }

    /**
     * 微信小程序订阅消息
     * @param userId
     */
    @Async
    public void sendAsyncMessageToMember(Long userId, String nickname, Long groupId) {
        // 有未读消息
        MemberConversationDO conversationDO = memberConversationMapper.getMemberConversationByByUserIdAndGroupId(userId, groupId);
        if(conversationDO.getUnreadMessageCount() < 2){
            boolean flag = socialClientApi.sendWxaSubscribeMessage(new SocialWxaSubscribeMessageSendReqDTO()
                    .setUserId(userId).setUserType(UserTypeEnum.MEMBER.getValue())
                    .setTemplateTitle(UNREAD_MESSAGE)
                    .setPage("pages/tabbar/index") // 首页
                    .addMessage("thing1", "你的朋友给你发了信息，点击查看") // 回信内容
                    .addMessage("time2", DateUtil.now())); // 互信时间
            if(!flag){
                mpUserApi.sendTemplateMsg(new UserMpTemplateMsgSendReqDTO()
                        .setUserId(userId)
                        .setUserType(UserTypeEnum.MEMBER.getValue())
                        .setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType())
                        .setMsgType(MpMessageSendTypeEnum.UNREAD_MSG.getType())
                        .setPage("pages/tabbar/index") // 首页
                        .addMessage("first", "您好，您收到一条私信回复")
                        .addMessage("keyword1", nickname)
                        .addMessage("keyword2", DateUtil.now())
                        .addMessage("keyword3", "您好，您收到一条私信回复")
                        .addMessage("remark", "本条信息是站内信通知！点击查看详情"));
            }
        }
    }

    @Override
    public void updateMemberMessage(MemberMessageSaveReqVO updateReqVO) {
        // 校验存在
        validateMemberMessageExists(updateReqVO.getId());
        // 更新
        MemberMessageDO updateObj = BeanUtils.toBean(updateReqVO, MemberMessageDO.class);
        memberMessageMapper.updateById(updateObj);
    }

    @Override
    public void updateMemberMessage(AppMemberMessageSaveReqVO updateReqVO) {
        // 校验存在
        validateMemberMessageExists(updateReqVO.getId());
        // 更新
        MemberMessageDO updateObj = BeanUtils.toBean(updateReqVO, MemberMessageDO.class);
        memberMessageMapper.updateById(updateObj);
    }

    @Override
    public void deleteMemberMessage(Long id) {
        // 校验存在
        validateMemberMessageExists(id);
        // 删除
        memberMessageMapper.deleteById(id);
    }

    private void validateMemberMessageExists(Long id) {
        if (memberMessageMapper.selectById(id) == null) {
            throw exception(MEMBER_MESSAGE_NOT_EXISTS);
        }
    }

    @Override
    public MemberMessageDO getMemberMessage(Long id) {
        return memberMessageMapper.selectById(id);
    }

    @Override
    public PageResult<MemberMessageDO> getMemberMessagePage(MemberMessagePageReqVO pageReqVO) {
        return memberMessageMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<MemberMessageDO> getMemberMessagePage(AppMemberMessagePageReqVO pageReqVO) {
        // 更新已读
        MemberConversationDO conversationDO = memberConversationMapper.getMemberConversationByByUserIdAndGroupId(pageReqVO.getSendUserId(), pageReqVO.getGroupId());
        if(conversationDO.getUnreadMessageCount() > 0){
            memberMessageMapper.updateMessageRead(pageReqVO.getGroupId(), conversationDO.getUserId());
            conversationDO.setUnreadMessageCount(0);
            memberConversationMapper.updateById(conversationDO);
        }

        return memberMessageMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateImMessageReadStatus(Long messageId, Long loginUserId) {
        MemberMessageDO memberMessage = memberMessageMapper.selectById(messageId);
        if(loginUserId.equals(memberMessage.getReceiveUserId())){
            memberMessage.setReadStatus(Boolean.TRUE);
            memberMessageMapper.updateById(memberMessage);
            getSelf().sendAsyncMessageToMember(memberMessage.getSendUserId(), WebSocketMessageTypeConstants.IM_MESSAGE_READ, memberMessage);
        }
    }

    @Override
    public int sendMemberSubscribeMsg() {
        MemberUserRespDTO userRespDTO = new MemberUserRespDTO();
        List<MemberUserRespDTO> memberList = memberUserApi.getUserListByUser(userRespDTO);
        for(MemberUserRespDTO member : memberList){
            getSelf().sendAsyncMessageToMember(member.getId());
        }
        return memberList.size();
    }

    @Async
    public void sendAsyncMessageToMember(Long userId) {
        // 有未读消息
        boolean flag = socialClientApi.sendWxaSubscribeMessage(new SocialWxaSubscribeMessageSendReqDTO()
                .setUserId(userId).setUserType(UserTypeEnum.MEMBER.getValue())
                .setTemplateTitle(UNREAD_MESSAGE)
                .setPage("pages/tabbar/index") // 首页
                .addMessage("thing1", "你的朋友给你发了信息，点击查看") // 回信内容
                .addMessage("time2", DateUtil.now())); // 互信时间
        if(!flag){
            mpUserApi.sendTemplateMsg(new UserMpTemplateMsgSendReqDTO()
                    .setUserId(userId)
                    .setUserType(UserTypeEnum.MEMBER.getValue())
                    .setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType())
                    .setMsgType(MpMessageSendTypeEnum.UNREAD_MSG.getType())
                    .setPage("pages/tabbar/index") // 首页
                    .addMessage("first", "您好，您收到一条私信回复")
                    .addMessage("keyword1", "匿名用户")
                    .addMessage("keyword2", DateUtil.now())
                    .addMessage("keyword3", "您好，您收到一条私信回复")
                    .addMessage("remark", "本条信息是站内信通知！点击查看详情"));
        }
    }

    private MemberMessageServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}